package cn.yto.print.service.impl;

import cn.hutool.json.JSONUtil;
import cn.yto.print.config.CaffeineCacheConfig;
import cn.yto.print.config.PrintFutureContext;
import cn.yto.print.enums.PluginRegistryEnum;
import cn.yto.print.enums.PrintCmdEnum;
import cn.yto.print.enums.ResponseCodeEnum;
import cn.yto.print.model.dto.PrintCacheDTO;
import cn.yto.print.model.request.KuaiShouPrintRequest;
import cn.yto.print.model.request.PrintTaskRequest;
import cn.yto.print.model.response.KuaiShouPrintResponse;
import cn.yto.print.model.response.PrintTaskResponse;
import cn.yto.print.plugins.SocketSessionContext;
import cn.yto.print.service.PrintConsumerService;
import cn.yto.print.service.handle.ITextMessageFunction;
import cn.yto.print.service.handle.MessageHandler;
import cn.yto.print.utils.PrintTaskUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Component
public class KuaiShouMessageHandler extends MessageHandler {

    private SocketSessionContext socketSessionContext;

    @Override
    public void initialize() {
        socketSessionContext = new SocketSessionContext(PluginRegistryEnum.KS, createMessageHandler());
    }

    @Override
    public ITextMessageFunction createMessageHandler() {
        return (session, message) -> {
            CaffeineCacheConfig.getHeartCache().put(PluginRegistryEnum.KS.getCode(), 0);
            KuaiShouPrintResponse kuaishouPrintResponse;
            try {
                kuaishouPrintResponse = JSONUtil.toBean(message.getPayload(), KuaiShouPrintResponse.class);
            } catch (Exception e) {
                log.error("快手打印组件回执报文解析异常，报文将丢弃~", e);
                return;
            }

            if (StringUtils.isEmpty(kuaishouPrintResponse.getCmd()) || "getClientInfo".equalsIgnoreCase(kuaishouPrintResponse.getCmd())) {
                return;
            }
            if (StringUtils.isEmpty(kuaishouPrintResponse.getTaskID())) {
                return;
            }
            PrintCacheDTO printCache = CaffeineCacheConfig.getPrintCache().getIfPresent(kuaishouPrintResponse.getTaskID());
            if (null == printCache) {
                log.error("快手打印任务 TaskID：{} 缓存不存在，报文将丢弃~", kuaishouPrintResponse.getTaskID());
                return;
            }

            if ("print".equalsIgnoreCase(kuaishouPrintResponse.getCmd()) && !"success".equalsIgnoreCase(kuaishouPrintResponse.getStatus())) {
                handlePrintFailedResponse(kuaishouPrintResponse, printCache);
                CaffeineCacheConfig.getPrintCache().invalidate(kuaishouPrintResponse.getTaskID());
            }
            if ("notifyPrintResult".equalsIgnoreCase(kuaishouPrintResponse.getCmd())) {
                handleNotifyPrintResultResponse(kuaishouPrintResponse, printCache);
                CaffeineCacheConfig.getPrintCache().invalidate(kuaishouPrintResponse.getTaskID());
            }
        };
    }

    private void handlePrintFailedResponse(KuaiShouPrintResponse response, PrintCacheDTO printCache) {
        PrintTaskResponse printTaskResponse = new PrintTaskResponse();
        printTaskResponse.setCmd(PrintCmdEnum.NOTIFY_PRINT_RESULT.getCmd());
        printTaskResponse.setTaskId(response.getTaskID());
        printTaskResponse.setSessionId(printCache.getSessionId());
        printTaskResponse.setTemplateType(printCache.getTemplateType());
        List<PrintTaskResponse.PrintStatus> printStatusList = Lists.newArrayList();
        printCache.getDocumentIds().forEach(documentId -> {
            PrintTaskResponse.PrintStatus failedStatus = new PrintTaskResponse.PrintStatus();
            failedStatus.setStatus(ResponseCodeEnum.FAILURE.getStatus());
            failedStatus.setDocumentID(documentId);
            failedStatus.setMsg(response.getMsg());
            printStatusList.add(failedStatus);
        });
        printTaskResponse.setPrintStatus(printStatusList);
        CompletableFuture<PrintTaskResponse> future = PrintFutureContext.getResponseFuture(response.getTaskID());
        if (null != future) {
            future.complete(printTaskResponse);
        } else {
            PrintConsumerService.handleTimeOutResponse(printTaskResponse);
        }
    }

    /**
     * 处理打印结果通知
     *
     * @param response
     * @param printCache
     */
    private void handleNotifyPrintResultResponse(KuaiShouPrintResponse response, PrintCacheDTO printCache) {

        PrintTaskResponse printTaskResponse = new PrintTaskResponse();
        printTaskResponse.setCmd(PrintCmdEnum.NOTIFY_PRINT_RESULT.getCmd());
        printTaskResponse.setTaskId(response.getTaskID());
        printTaskResponse.setSessionId(printCache.getSessionId());
        printTaskResponse.setTemplateType(printCache.getTemplateType());
        if (CollectionUtils.isNotEmpty(response.getPrintStatus())) {
            List<PrintTaskResponse.PrintStatus> printStatusList = Lists.newArrayList();
            for (KuaiShouPrintResponse.PrintStatus printStatus : response.getPrintStatus()) {
                PrintTaskResponse.PrintStatus newStatus = new PrintTaskResponse.PrintStatus();
                newStatus.setStatus(printStatus.getStatus());
                newStatus.setDocumentID(printStatus.getDocumentID());
                newStatus.setDetail(printStatus.getDetail());
                printStatusList.add(newStatus);
            }
            printTaskResponse.setPrintStatus(printStatusList);
        }
        CompletableFuture<PrintTaskResponse> future = PrintFutureContext.getResponseFuture(response.getTaskID());
        if (null != future) {
            future.complete(printTaskResponse);
        } else {
            PrintConsumerService.handleTimeOutResponse(printTaskResponse);
        }
    }

    @Override
    public KuaiShouPrintRequest buildPrintRequest(PrintTaskRequest pluginPrintParam) {
        KuaiShouPrintRequest.Task kuaiShouTask = new KuaiShouPrintRequest.Task();
        kuaiShouTask.setTaskID(pluginPrintParam.getTaskId());
        kuaiShouTask.setPreview(false);
        kuaiShouTask.setPrinter(pluginPrintParam.getPrinter());
        kuaiShouTask.setFirstDocumentNumber(pluginPrintParam.getFirstDocumentNumber());
        kuaiShouTask.setTotalDocumentCount(pluginPrintParam.getTotalDocumentCount());
        List<KuaiShouPrintRequest.Document> documentList = new ArrayList<>();
        pluginPrintParam.getDocuments().forEach(x -> {
            KuaiShouPrintRequest.Document document = new KuaiShouPrintRequest.Document();
            List<Map<String, Object>> contents = new ArrayList<>();
            Map<String, Object> printDataMap = JSONUtil.toBean(x.getPrintData(), Map.class);
            if (!ObjectUtils.isEmpty(x.getAddData())) {
                printDataMap.put("addData", x.getAddData());
            }
            contents.add(printDataMap);
            contents.add(x.getCustomData());
            document.setContents(contents);
            document.setDocumentID(x.getDocumentID());
            document.setKsOrderFlag(x.getKsOrderFlag());
            document.setWaybillCode(x.getWaybillCode());
            documentList.add(document);
        });
        kuaiShouTask.setDocuments(documentList);
        KuaiShouPrintRequest kuaiShouPrintRequest = new KuaiShouPrintRequest();
        kuaiShouPrintRequest.setCmd("print");
        kuaiShouPrintRequest.setRequestID(pluginPrintParam.getTaskId());
        kuaiShouPrintRequest.setVersion("1.0");
        kuaiShouPrintRequest.setTask(kuaiShouTask);
        return kuaiShouPrintRequest;
    }

    @Override
    public CompletableFuture<PrintTaskResponse> processMessage(PrintTaskRequest printParam) {
        KuaiShouPrintRequest caiNiaoPrintRequest = buildPrintRequest(printParam);
        socketSessionContext.sendPrintRequest(caiNiaoPrintRequest);
        PrintTaskUtil.addPrintTaskCache(printParam);
        CompletableFuture<PrintTaskResponse> completableFuture = new CompletableFuture<>();
        PrintFutureContext.addResponseFuture(printParam.getTaskId(), completableFuture);
        return completableFuture;
    }

    @Override
    public SocketSessionContext getSocketContext() {
        return socketSessionContext;
    }

}
